package com.gitee.wsl.platform

import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.staticCFunction
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import platform.posix.SIGINT
import platform.posix.signal
import platform.posix.usleep
import kotlin.experimental.ExperimentalNativeApi
import kotlin.native.concurrent.ObsoleteWorkersApi
import kotlin.native.concurrent.Worker
import kotlin.system.exitProcess
import kotlin.time.Duration.Companion.milliseconds

// Could be there's another way to do this, but the native signal handler is really restrictive.
// Instead, I spawn up a background coroutine which polls a static value which gets set by CTRL-C.
// Then, it sets another static value which the signal handler checks for.
private const val PULSE_MS = 100

private var interrupted = false
private var shutdownCountdown = 10 // Give the dispose coroutine a chance to wake up, run, and finish


actual val platform: Platform
    get() = NativePlatform

object NativePlatform:Platform{
    @OptIn(ExperimentalNativeApi::class)
    override val current: PlatformType
        get() {
            val arch = kotlin.native.Platform.cpuArchitecture.asArch()
            val version = kotlin.native.Platform.programName?:""
            return when(kotlin.native.Platform.osFamily){
                OsFamily.IOS -> PlatformType.OS.IOS(arch,  version=version)
                OsFamily.ANDROID -> PlatformType.OS.Android(arch)
                OsFamily.MACOSX -> PlatformType.OS.Desktop.MacOs(arch, version)
                OsFamily.LINUX -> PlatformType.OS.Desktop.Linux(arch, version)
                OsFamily.WINDOWS -> PlatformType.OS.Desktop.Windows(arch, version)
                OsFamily.TVOS -> PlatformType.OS.TvOs(arch, version=version)
                OsFamily.WATCHOS -> PlatformType.OS.WatchOs(arch,  version=version)
                OsFamily.WASM -> PlatformType.Wasm(arch,version)
                else -> PlatformType.OS.Unknown(arch, version)
            }
        }


    @OptIn(ExperimentalForeignApi::class)
    override fun onShutdown(dispose: () -> Unit) {
        CoroutineScope(Dispatchers.Default).launch {
            while (!interrupted) {
                delay(PULSE_MS.milliseconds)
            }
            dispose()
            shutdownCountdown = 0
        }

        signal(SIGINT, staticCFunction<Int, Unit> {
            if (interrupted) return@staticCFunction // Already called previously

            interrupted = true
            while (shutdownCountdown > 0) {
                usleep((PULSE_MS * 1000).toUInt())
                shutdownCountdown--
            }

            exitProcess(130) // 130 == 128+2, where 2 == SIGINT
        })
    }

    @OptIn(ObsoleteWorkersApi::class)
    fun getCurrentThreadName(): String =
        Worker.current.name // TODO: use native C implementation

}